package com.virecog.symbol;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;

import processing.core.PApplet;
import processing.core.PImage;

import com.virecog.utils.Constains;
import com.virecog.utils.Utils;

public class SymbolDetection {

	PApplet applet;


	String filepath;

	PImage picture;

	ArrayList<Line> lines = new ArrayList<Line>();
	int current_line_index = 0;
	Line current_line;

	int countSym = 0;

	/**
	 * 
	 * @param filename
	 */
	public SymbolDetection(String filepath) {
		// TODO Auto-generated constructor stub
		this.filepath = filepath;
		applet = new PApplet();

		// load picture
		loadPicture();

		// symbol identify
		System.out.println(picture.width + " " + picture.height);
		identifyLines();

	}

	/**
	 * 
	 * @param img
	 */
	public SymbolDetection(PImage img) {

		applet = new PApplet();
		picture = img;

		System.out.println(picture.width + " " + picture.height);
		identifyLines();
	}

	/**
	 * load picture
	 */
	public void loadPicture() {
		picture = applet.loadImage(filepath);
	}

	/***** identify line and convert symbol image to matrix value *****/

	/**
	 * identify line
	 */
	public void identifyLines() {

		int width = picture.width;
		int height = picture.height;
		boolean line_present = true;

		int top = 0, bottom = 0;
		int x = 0, y = 0;

		while (line_present) {

			// identify the top of line
			x = 0;
			while (picture.get(x, y) == applet.color(255, 255, 255)) {
				x++;
				if (x == width) {
					y++;
					x = 0;
				}
				if (y >= height) {
					line_present = false;
					break;
				}
			}
			if (line_present) {

				top = y;

				// identify bottom of line
				boolean no_black_pixel = false;
				while (!no_black_pixel) {

					y++;
					no_black_pixel = true;
					for (x = 0; x < width; x++) {
						if (picture.get(x, y) != applet.color(255, 255, 255)) {
							no_black_pixel = false;
						}
					}
				}
				bottom = y - 1;

				// new line identified
				Line line = new Line(top, bottom);
				line = identifyWords(line);
				lines.add(line);
			}

		}
	}

	/**
	 * identify words
	 * 
	 * @param line
	 * @return
	 */
	public Line identifyWords(Line line) {

		ArrayList<Word> words = new ArrayList<Word>();
		ArrayList<Symbol> symbols = new ArrayList<Symbol>();

		int top = line.top;
		int bottom = line.bottom;
		int left, right;

		int x, y, count;
		boolean word_present = true;

		x = 0;
		y = top;

		while (word_present) {

			// left bound
			count = 0;// dem so pixel cua khoang cach giua cac ki tu
			y = top;
			while (picture.get(x, y) == applet.color(255, 255, 255)) {
				y++;
				if (y == bottom) {
					x++;
					y = top;
					count++;
				}
				if (x >= picture.width) {
					word_present = false;
					break;
				}
			}

			// neu phat hien ra ki tu space hoac ket thuc dong
			if (count >= Constains.SPASE_WIDE || !word_present) {
				// new word
				Word w = new Word();
				w.setSymbols(symbols);
				if (symbols.size() != 0) {
					words.add(w);
				}

				// initial new symbol list for new word
				symbols = new ArrayList<Symbol>();

			}

			// right bound
			if (word_present) {
				left = x;

				boolean no_black_pixel = false;
				while (!no_black_pixel) {
					x++;
					no_black_pixel = true;
					for (y = top; y <= bottom; y++) {
						if (picture.get(x, y) != applet.color(255, 255, 255)) {
							no_black_pixel = false;
							break;
						}
					}
				}
				right = x - 1;

				// new symbol identified
				Symbol symbol = new Symbol(top, right, bottom, left);

				Symbol sym = identifyCharacterBounds(symbol);

				if (sym != null) {
					symbols.add(sym);
					countSym++;
				}
			}
		}
		line.setWords(words);
		return line;
	}

	/**
	 * identify character bound
	 * 
	 * @param s
	 * @return
	 */
	public Symbol identifyCharacterBounds(Symbol s) {

		int x = s.left;
		int y = s.top;
		boolean character_present = true;
		// top
		while (picture.get(x, y) == applet.color(255, 255, 255)) {
			x++;
			if (x == s.right) {
				y++;
				x = s.left;
			}
			if (y >= s.bottom) {
				character_present = false;
				break;
			}
		}
		if (character_present) {
			// bottom

			s.top = y;
			y = s.bottom;
			x = s.left;
			while (picture.get(x, y) == applet.color(255, 255, 255)) {
				x++;
				if (x == s.right) {
					y--;
					x = s.left;
				}
				if (y <= s.top) {
					character_present = false;
					break;
				}
			}
			s.bottom = y;

			// left
			x = s.left;
			y = s.top;
			while (picture.get(x, y) == applet.color(255, 255, 255)) {
				y++;
				if (y == s.bottom) {
					x++;
					y = s.top;
				}
				if (x >= s.right) {
					character_present = false;
					break;
				}
			}

			// right
			if (character_present) {
				s.left = x;
				x = s.right;
				y = s.top;
				while (picture.get(x, y) == applet.color(255, 255, 255)) {
					y++;
					if (y == s.bottom) {
						x--;
						y = s.top;
					}
					if (x <= s.left) {
						character_present = false;
						break;
					}
				}

				s.right = x;
			} else {// not symbol
				return null;
			}
		} else {// not symbol
			return null;
		}

		return s;
	}

	/**
	 * resize detected symbol image to image with size 10x15
	 * 
	 * @param sym
	 *            symbol image
	 * @return resized image
	 */
	public PImage resizeSymbolImage(Symbol sym) {
		PImage img = picture.get(sym.left, sym.top, sym.right - sym.left + 1,
				sym.bottom - sym.top + 1);
		img.resize(10, 15);
		return img;
	}

	/**
	 * calculate average color
	 * 
	 * @param img
	 * @return
	 */
	public int middleColor(PImage img) {
		int sum = 0;
		for (int j = 0; j < img.height; j++) {
			for (int i = 0; i < img.width; i++) {
				sum += img.get(i, j);
			}
		}
		return sum / (img.width * img.height);
	}

	/**
	 * 
	 * @param img
	 * @return
	 */
	public double[] symbolImageToMatrixValue(PImage img) {

		double matrix[] = new double[150];

		int middle = middleColor(img);
		for (int j = 0; j < img.height; j++) {
			for (int i = 0; i < img.width; i++) {

				if (img.get(i, j) >= middle) {
					matrix[i + j * img.width] = 0;
				} else {
					matrix[i + j * img.width] = 1;
				}
			}
		}
		return matrix;

	}

	/**
	 * input for encog neural network
	 * 
	 * @return training input data
	 */
	public double[][] getInputForEncogNetwork() {
		int i = 0;
		System.out.println("Sym number:" + countSym);
		double[][] train = new double[countSym][150];
		for (Line l : lines) {
			ArrayList<Word> words = l.getWords();
			for (Word word : words) {
				ArrayList<Symbol> symbols = word.getSymbols();

				for (Symbol symbol : symbols) {

					PImage img = resizeSymbolImage(symbol);

					double[] input = symbolImageToMatrixValue(img);
					train[i++] = input;
				}
			}
		}
		return train;
	}

	// training output data
	/**
	 * desired output for encog neural network
	 * 
	 * @param desireTextPath
	 * @return
	 */
	public double[][] getOutputForEncogNetwork(String desireTextPath) {
		String desireText = readDesireResult(desireTextPath);
		double[][] outputs = new double[desireText.length()][16];
		System.out.println("Desire size:" + desireText.length());
		for (int i = 0; i < desireText.length(); i++) {
			double[] output = Utils.charToBitarray(desireText.charAt(i));
			outputs[i] = output;
		}

		return outputs;
	}

	/**
	 * read desire text
	 * 
	 * @param filepath
	 * @return
	 */
	public String readDesireResult(String filepath) {
		File file = new File(filepath);
		{
			// FileInputStream inf = new FileInputStream(file);
			StringBuffer buffer = new StringBuffer();
			try {
				BufferedReader reader = new BufferedReader(new FileReader(file));
				String line;
				while ((line = reader.readLine()) != null) {
					if (line != null)
						buffer.append(line);
				}
				return buffer.toString();
			} catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				return null;
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				return null;
			}
		}
	}

	/**
	 * 
	 * @param args
	 */
	public String getRecognitionResult() {

		String result = "";
		for (Line l : lines) {
			result += l.getRecognitionLine() + "/n";
		}
		return result;

	}

	/********************* test function ******************************/
	/**
	 * 
	 * @param desireTextPath
	 * @throws IOException
	 */
	public void savetraindata(String desireTextPath) throws IOException {
		int i = 0;
		File file = new File("testdata.txt");

		BufferedWriter berwriter = new BufferedWriter(new FileWriter(file));
		PrintWriter writer = new PrintWriter(berwriter);

		String desireText = readDesireResult(desireTextPath);
		System.out.println(desireText.length());

		System.out.println("Set training data ...");
		for (Line l : lines) {
			ArrayList<Word> words = l.getWords();
			for (Word word : words) {
				ArrayList<Symbol> symbols = word.getSymbols();

				for (Symbol symbol : symbols) {
					PImage img = resizeSymbolImage(symbol);

					double[] input = symbolImageToMatrixValue(img);
					char ch = desireText.charAt(i++);
					double[] output = Utils.charToBitarray(ch);

					System.out.println("input length:" + input.length);
					System.out.println("output length:" + output.length);

					for (int j = 0; j < input.length; j++) {
						writer.print(input[j]);
						writer.print(",");
					}

					for (int j = 0; j < output.length; j++) {
						writer.print(output[j]);
						writer.print(",");
					}
					writer.println();
				}
			}
		}
		writer.close();
	}

	/**
	 * 
	 */
	public void saveSymbolImage() {
		int i = 0;
		for (Line l : lines) {
			ArrayList<Word> words = l.getWords();
			for (Word word : words) {
				ArrayList<Symbol> symbols = word.getSymbols();

				for (Symbol symbol : symbols) {
					PImage img = resizeSymbolImage(symbol);
					saveImageToFile(img, "symbol" + i + ".png");
					i++;
				}
			}
		}
	}

	/**
	 * 
	 * @param img
	 * @param filename
	 */
	public void saveImageToFile(PImage img, String filename) {
		Utils.saveImage(img, filename);
		System.out.println("Saved " + filename);
		System.out.println("");
	}

	public void testlines() {
		for (Line l : lines) {
			System.out.println(l.toString());
		}
	}

	/* ********************* End test************************ */

}
